﻿using System;
using System.Collections.Generic;
using UnityEngine;

namespace NodeEditor
{
    public class RuntimeTree
    {
        public Tree tree { get; private set; }
        public RuntimeValue runtimeValue = new RuntimeValue();
        readonly List<RuntimeNode[]> runtimeNodes = new List<RuntimeNode[]>();

        public RuntimeTree(Tree tree)
        {
            this.tree = tree.Clone();
            Dictionary<string, RuntimeNode> runtimeDict = new Dictionary<string, RuntimeNode>();
            Dictionary<string, AbstractNode> dict = new Dictionary<string, AbstractNode>();
            Dictionary<string, string> portDict = new Dictionary<string, string>();
            this.tree.nodes.ForEach(node =>
            {
                runtimeDict.Add(node.guid, new RuntimeNode(node));
                dict.Add(node.guid, node);
                node.ports.ForEach(port =>
                {
                    portDict.Add(port.guid, port.name);
                    dict.Add(port.guid, node);
                });
            });



            foreach (var node in this.tree.nodes)
            {
                var runtimeNode = runtimeDict[node.guid];
                foreach (var port in node.ports)
                {
                    if (port.direction == NodeEditor.PortDirection.Input)
                        continue;
                    foreach (var guid in port.childs)
                    {
                        var nodeGuid = dict[guid].guid;
                        var childNode = runtimeDict[nodeGuid];

                        var input = childNode;
                        var output = runtimeNode;
                        RuntimeEdge.Connect(portDict[guid], port.name,  input, output);
                    }
                }
            }

            var rootNode = runtimeDict[this.tree.root.guid];
            List<RuntimeNode> list = new List<RuntimeNode>();

            foreach (var node in rootNode.outputs.GetAllNodes())
            {
                list.Add(node);
            }
            rootNode.Clear();

            foreach (var item in list)
            {
                runtimeNodes.Add(item.ForEach());
            }
        }

        public void ForEach(Action<RuntimeNode> action)
        {
            foreach (var nodes in runtimeNodes)
            {
                TreeForEach(nodes, action);
            }
        }

        void TreeForEach(RuntimeNode[] nodes, Action<RuntimeNode> action)
        {
            Dictionary<RuntimeNode, bool> stateDict = new Dictionary<RuntimeNode, bool>();

            foreach (var item in nodes)
            {
                stateDict.Add(item, true);
            }
            
            foreach (var runtimeNode in nodes)
            {
                var state = stateDict[runtimeNode];
                if(state)
                {
                    action?.Invoke(runtimeNode);
                }

                CheckConditionNode(runtimeNode, nodes, stateDict);
            }

        }

        void CheckConditionNode(RuntimeNode runtimeNode, RuntimeNode[] nodes, Dictionary<RuntimeNode,bool> dict)
        {
            if (!(runtimeNode.node is ConditionNode n))
                return;

            var key = n.result ? ConditionNode.falseString : ConditionNode.trueString;
            var array = runtimeNode.GetConnectNode(key);
            foreach (var connectNode in array)
            {
                var list = nodes.BFSForEach(connectNode, (a, b) => b != runtimeNode && a.ContainsConnect(b));
                foreach (var item in list)
                {
                    dict[item] = false;
                }
            }
        }
    }
}